package com.example.runnable;

import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.StrUtil;
import com.example.bean.DeviceConf;
import com.example.constant.CommonConst;
import com.example.manager.*;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.concurrent.*;

/**
 * 类描述
 *
 * @author: Fred
 * @email 453086@qq.com
 * @create: 2024-10-19 16:07
 */
@Slf4j
public class CollectRunnable implements Runnable {

    private final CollectManager collectManager = CollectManager.getInstance();
    private final AisManager aisManager = AisManager.getInstance();
    private final RadarManager radarManager = RadarManager.getInstance();
    private final WaterManager waterManager = WaterManager.getInstance();

    private final DeviceConf deviceConf;
    private volatile boolean running = true;

    private Socket socket;
    private DatagramSocket udpSocket;

    private PrintWriter out;
    private ScheduledExecutorService heartBeatScheduler;

    public CollectRunnable(DeviceConf deviceConf) {
        this.deviceConf = deviceConf;
        initializeConnection();
    }

    /**
     * 根据协议类型初始化连接
     */
    private void initializeConnection() {
        DeviceConf.ProtocolEnum protocol = deviceConf.getProtocol();
        try {
            if (protocol == DeviceConf.ProtocolEnum.TCP) {
                initializeTcpConnection(deviceConf.getIp(), deviceConf.getPort());
            } else if (protocol == DeviceConf.ProtocolEnum.UDP) {
                initializeUdpConnection(deviceConf.getPort() + Integer.parseInt(deviceConf.getSecret()));
            } else {
                log.error("不支持的协议类型：{}", protocol);
            }
        } catch (IOException e) {
            log.error("连接{}服务器失败：{}，尝试重连...", protocol, e.getMessage());
            if (protocol.equals(DeviceConf.ProtocolEnum.TCP)) {
                tryReconnect();
            }
        }
    }

    private void initializeTcpConnection(String ip, int port) throws IOException {
        socket = new Socket(ip, port);
        out = new PrintWriter(socket.getOutputStream(), true);
        collectManager.put(deviceConf.getSecret(), this);

        log.info("已连接到TCP服务器：{}:{}", ip, port);
        startHeartbeat();
    }

    private void initializeUdpConnection(int port) throws IOException {
        udpSocket = new DatagramSocket(port);
        udpSocket.setBroadcast(true);
        collectManager.put(deviceConf.getSecret(), this);

        log.info("已连接到UDP服务器：{}:{}", deviceConf.getIp(), port);
        startHeartbeat();
    }

    /**
     * 启动心跳机制
     */
    private void startHeartbeat() {
        heartBeatScheduler = Executors.newSingleThreadScheduledExecutor();
        heartBeatScheduler.scheduleAtFixedRate(() ->
                sendHeartbeat(deviceConf.getProtocol()), 0, CommonConst.HEARTBEAT_INTERVAL, TimeUnit.SECONDS);
    }


    private void sendHeartbeat(DeviceConf.ProtocolEnum protocol) {
        try {
            if (protocol == DeviceConf.ProtocolEnum.TCP && socket != null && !socket.isClosed()) {
                out.println("HEARTBEAT");
                log.info("TCP 心跳包已发送");
            } else if (protocol == DeviceConf.ProtocolEnum.UDP && udpSocket != null && !udpSocket.isClosed()) {
                byte[] heartBeatMessage = "HEARTBEAT".getBytes(StandardCharsets.UTF_8);
                sendCommand(heartBeatMessage);
                log.info("UDP 心跳包已发送");
            }
        } catch (Exception e) {
            log.error("心跳包发送失败：{}", e.getMessage());
        }
    }

    /**
     * 发送命令
     */
    private void sendCommand(byte[] command) {
        try {
            InetAddress address = InetAddress.getByName(deviceConf.getIp());
            DatagramPacket packet = new DatagramPacket(command, command.length, address, deviceConf.getPort());
            udpSocket.send(packet);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    private void tryReconnect() {

        // 先关闭当前连接
        closeResources();

        String ip = deviceConf.getIp();
        int port = deviceConf.getPort();

        while (running) {
            try {
                socket = new Socket(ip, port);
                collectManager.put(deviceConf.getSecret(), this);
                log.info("已连接到服务器：{}:{}", ip, port);

                // 重新启动心跳机制
                out = new PrintWriter(socket.getOutputStream(), true);
                startHeartbeat();
                return;
            } catch (IOException e) {
                log.error("重连服务器：{}:{} 失败，等待 {} 秒后重试：{}", ip, port, CommonConst.RECONNECT_INTERVAL, e.getMessage());
                ThreadUtil.sleep(CommonConst.RECONNECT_INTERVAL * 1000);
            }
        }
    }

    @Override
    public void run() {
        DeviceConf.DeviceEnum device = deviceConf.getDevice();
        if (device == DeviceConf.DeviceEnum.RADAR) {
            readRadarMessage();
        }
        if (device == DeviceConf.DeviceEnum.AIS) {
            readAisMessage();
        }
        if (device == DeviceConf.DeviceEnum.WATER) {
            readWaterMessage();
        }
    }

    private void readRadarMessage() {
        byte[] buffer = new byte[1024];
        try {
            while (running) {
                DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
                udpSocket.receive(packet);
                byte[] data = packet.getData();
                //心跳
                if (data.length == 7) {
                    log.info("心跳消息");
                    continue;
                }
                radarManager.ParseRadarMessage(data, deviceConf);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    private void readAisMessage() {
        while (running) {
            try (BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {
                String message;
                while ((message = in.readLine()) != null) {
                    // 根据设备类型判断并处理数据
                    if (!StrUtil.containsAnyIgnoreCase(message, CommonConst.GPS_PREFIX, CommonConst.AIS_VDO_PREFIX)) {
                        log.info(LocalDateTime.now() + " | message = " + message);
                        aisManager.parseAISMessage(message);
                    }
                }
            } catch (IOException e) {
                // 捕获异常并记录日志
                log.error("读取设备：{} 数据时发生错误：{}", deviceConf.getDeviceName(), e.getMessage());
                tryReconnect();
            }
        }
    }

    private void readWaterMessage() {

        while (running) {
            try (InputStream inputStream = socket.getInputStream()) {

                byte[] buffer = new byte[1024];
                int bytesRead;
                StringBuilder dataCache = new StringBuilder();

                while ((bytesRead = inputStream.read(buffer)) != -1) {
                    for (int i = 0; i < bytesRead; i++) {
                        dataCache.append(String.format("%02X", buffer[i]));
                    }
                    // 处理缓存中的完整数据包
                    waterManager.processPackets(dataCache);
                }
            } catch (Exception e) {
                log.error("读取设备：{} 数据时发生错误：{}", deviceConf.getDeviceName(), e.getMessage());
                tryReconnect();
            }
        }
    }

    /**
     * 关闭连接和资源
     */
    private void closeResources() {
        try {
            if (heartBeatScheduler != null && !heartBeatScheduler.isShutdown()) {
                heartBeatScheduler.shutdownNow();
            }
            closeQuietly(out);
            closeQuietly(socket);
            closeQuietly(udpSocket);
        } catch (Exception e) {
            log.error("关闭设备：{} 资源时发生错误：{}", deviceConf.getDeviceName(), e.getMessage());
        }
    }

    private void closeQuietly(Closeable closeable) {
        if (closeable != null) {
            try {
                closeable.close();
            } catch (IOException e) {
                log.warn("关闭资源时发生错误：{}", e.getMessage());
            }
        }
    }

    public void stop() {
        running = false;
        closeResources();
        log.info("设备：{} 的采集线程已停止", deviceConf.getDeviceName());
    }
}